
package main

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/gorilla/websocket"
	"github.com/satori/go.uuid"
	"log"
	"sync"
	"time"
	"net"
)

type ClientManager struct {
	broadcast  chan *Client
	register   chan *Client
	registerForRoom   chan *Client
	unregister chan *Client
	timeout chan *Client
}


type Client struct {
	id string
	roomId int
	serverName string
	conn *websocket.Conn
	send chan []byte
	data []byte
	accessTime time.Time
}

type RoomManager struct {
	rooms []*Room
	serverName string
	register  chan *Client
}

type Room struct {
	id int
	clients chan *Client
	clientsInRoom map[*Client]bool
	mutex *sync.RWMutex
}


type Message struct {
	Sender    string `json:"sender,omitempty"`
	Recipient string `json:"recipient,omitempty"`
	Content   string `json:"content,omitempty"`
	RoomId int `json:roomid,omitempty`
}



var roomManger *RoomManager
var clientManager *ClientManager
var serverName = ":13347"
//房间数
var roomCount = 10
//房间人数
var roomCountPer = 20



func init()  {

	roomManger = &RoomManager{
		rooms : make([]*Room, roomCount),
		serverName : serverName,
		register:   make(chan *Client),
	}

	 clientManager = &ClientManager{
		broadcast:  make(chan *Client),
		register:   make(chan *Client, roomCount * roomCountPer),
		registerForRoom:   make(chan *Client),
		unregister: make(chan *Client),
		timeout: make(chan *Client),
	}

	//每个聊天室容纳的用户数是10人
	for i := range roomManger.rooms {
		roomManger.rooms[i] = &Room{id : i, clients:make(chan *Client, roomCountPer), clientsInRoom:make(map[*Client]bool),mutex:&sync.RWMutex{}}
		go roomManger.rooms[i].listenClient()
		go roomManger.rooms[i].heartbeatClient()
	}



}

func (room *Room) heartbeatClient()  {
	for {
		time.Sleep(time.Second)
		room.mutex.RLock()
		log.Printf("clientsInRoom--%d, register--%d\n",len(room.clientsInRoom), len(clientManager.register))
		for client,_ := range room.clientsInRoom {
			//log.Println(client.accessTime, client.accessTime.Add(30 * time.Second).Before(time.Now()), len(room.clientsInRoom), len(clientManager.register))
			if client.accessTime.Add(30 * time.Second).Before(time.Now()) {
				jsonMessage, _ := json.Marshal(&Message{Sender: client.id, Content: string("任务超时") + "---" + serverName, RoomId:client.roomId})
				client.data = jsonMessage
				clientManager.timeout <- client
			}
		}
		room.mutex.RUnlock()

	}
	log.Println("heartbeat.....")
}


func (room *Room) listenClient( ) {

	for {
		time.Sleep(time.Millisecond * 50)
		room.mutex.Lock()
		if len(room.clientsInRoom) < roomCountPer {
			client := <- clientManager.registerForRoom

			//log.Printf("server:%s, client:%s, roomId:%d\n", serverName, client.id, client.roomId )
			//select {
			//case room.clients <- client:
				client.roomId = room.id
				room.clientsInRoom[client] = true
				go client.read()
				go client.write()
				jsonMessage, _ := json.Marshal(&Message{Sender: client.id, Content: "/A new client has connected." + "---" + serverName, RoomId:room.id})
				log.Printf("%s", jsonMessage)
				go sendMsg(room, client, true, jsonMessage)
			//}
		}
		room.mutex.Unlock()
	}

}


func sendMsg(room *Room, client *Client, ingore bool, jsonMessage []byte) {
	room.mutex.RLock()
	for k, _ := range room.clientsInRoom {
		if !ingore || k != client {
			k.send <- jsonMessage
		}
	}
	room.mutex.RUnlock()
}



func main() {

	fmt.Printf("Starting application...%sServer...\n", serverName)
	go clientManager.start()
	http.HandleFunc("/ws", wsPage)
	http.ListenAndServe(serverName, nil)

}

func (clientManager *ClientManager) start() {
	for {
		select {
		case client := <-clientManager.unregister:
			room := roomManger.rooms[client.roomId]
			//<-room.clients
			<-clientManager.register
			room.mutex.Lock()
			delete(room.clientsInRoom, client)
			room.mutex.Unlock()
			jsonMessage, _ := json.Marshal(&Message{Sender: client.id, Content: "/A client has disconnected." + "---" + serverName, RoomId:room.id})
			log.Printf("unregister--%s--%d", jsonMessage, len(clientManager.register))
			go sendMsg(room, client, true, jsonMessage)
		case client := <-clientManager.broadcast:
			room := roomManger.rooms[client.roomId]
			go sendMsg(room, client, false, client.data)
		case client := <-clientManager.timeout:
			room := roomManger.rooms[client.roomId]
			<-clientManager.register
			room.mutex.Lock()
			delete(room.clientsInRoom, client)
			room.mutex.Unlock()
			client.send <- client.data
			close(client.send)
			log.Printf("%s--%d", "timeout", len(clientManager.register))
		}
	}
}


func wsPage(res http.ResponseWriter, req *http.Request) {

	conn, error := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(res, req, nil)
	if error != nil {
		http.NotFound(res, req)
		return
	}
	uuids, _ := uuid.NewV4()
	client := &Client{id: uuids.String(), conn: conn, send: make(chan []byte), serverName: serverName, accessTime:time.Now()}

	//clientManager.register <- client

	select {
	case clientManager.register <- client:
		log.Println("conn success")
		clientManager.registerForRoom <- client
	case <-time.After(10 * time.Second):
		conn.WriteMessage(websocket.TextMessage, []byte("请稍候登陆---" + client.id))
		conn.Close()
	}


}




func (client *Client) read() {

	for {
		_, message, err := client.conn.ReadMessage()
		if err != nil {
			switch e :=err.(type) {
			case *net.OpError:
				log.Println(e)
			case *websocket.CloseError:
				log.Println(e)
				clientManager.unregister <- client
				client.conn.Close()
			}
			break
		}
		client.accessTime = time.Now()
		jsonMessage, _ := json.Marshal(&Message{Sender: client.id, Content: string(message) + "---" + serverName, RoomId:client.roomId})
		client.data = jsonMessage
		clientManager.broadcast <- client
	}
}


func (client *Client) write() {

	defer func() {
		client.conn.Close()
	}()

	for {
		select {
		case message, ok := <-client.send:
			if !ok {
				client.conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			client.conn.WriteMessage(websocket.TextMessage, message)
		}
	}
}

